package Utils

import (
	"bufio"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
	"strings"
	"time"
)

const (
	FOMAT_MDY     = "01-02-2006"
	FOMAT_DMY     = "01-02-2006"
	FOMAT_YMD     = "2006-01-02"
	FOMAT_PYMD    = "2006.01.02"
	FOMAT_YMDHMS  = "2006.01.02 15:04:05"
	FOMAT_PYMDHMS = "2006.01.02 15:04:05"
	FOMAT_YMDHMSM = "2006-01-02 15:04:05.000000"
	FOMAT_YMDHMSN = "2006-01-02 15:04:05.000000000"
)

func TodayStart() time.Time {
	now := time.Now()
	return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
}

func TodayEndT() time.Time {
	now := time.Now()
	return time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 999999999, now.Location())
}

func MonthStart() time.Time {
	now := time.Now()
	return time.Date(now.Year(), now.Month(), 1, 23, 59, 59, 999999999, now.Location())
}

func MonthEnd() time.Time {
	return MonthStart().AddDate(0, 1, -1)
}

func FileSize(filePath string) (int64, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return 0, err
	}
	defer f.Close()
	info, err := f.Stat()

	if err != nil {
		return 0, err
	}
	return info.Size(), nil
}

func FileMd5(filePath string) (string, error) {
	f, err := os.Open(filePath)
	if nil != err {
		return "", err
	}
	defer f.Close()

	md5Handle := md5.New()
	_, err = io.Copy(md5Handle, f)
	if nil != err {
		return "", err
	}
	mdByte := md5Handle.Sum(nil) //计算 MD5 值，返回 []byte
	return fmt.Sprintf("%x", mdByte), nil
}

func BytesMd5(s []byte) string {
	m := md5.New()
	m.Write(s)
	md5Str := hex.EncodeToString(m.Sum(nil))
	return md5Str
}

func FileLines(filePath string) ([]*[]byte, error) {
	f, err := os.Open(filePath)
	if nil != err {
		return nil, err
	}
	defer f.Close()
	buf := bufio.NewReader(f)
	var lines []*[]byte
	for {
		//遇到\n结束读取
		b, errR := buf.ReadBytes('\n')
		if errR != nil {
			if errR == io.EOF {
				break
			}
			return nil, errR
		}
		lines = append(lines, &b)
	}
	return lines, nil
}

func FileStrLines(filePath string) ([]string, error) {

	bytesArr, err := FileLines(filePath)
	if err != nil || bytesArr == nil {
		return nil, err
	}
	var lines []string
	for _, v := range bytesArr {
		lines = append(lines, string(*v))
	}
	return lines, nil
}

func Encode(str string) []byte {
	return []byte(str)
}

func Decode(bytesStr []byte) string {
	return string(bytesStr)
}

func JsonEncode(v interface{}) ([]byte, error) {
	return json.Marshal(v)
}

func JsonDecode(data []byte, v interface{}) error {
	return json.Unmarshal(data, v)
}

func AssertInt(val interface{}) (int, error) {
	var newVal int
	switch v := val.(type) {
	case int:
		newVal = v
	case int8:
		newVal = int(v)
	case int32:
		newVal = int(v)
	case int64:
		newVal = int(v)
	case uint8:
		newVal = int(v)
	case uint16:
		newVal = int(v)
	case uint32:
		newVal = int(v)
	case uint64:
		newVal = int(v)
	default:
		return 0, errors.New(fmt.Sprintf("Assert Error: value types:%T cannot trans int", val))
	}

	return newVal, nil
}

func AssertByteList(val interface{}) ([]byte, error) {
	var newVal []byte
	switch v := val.(type) {
	case []byte:
		newVal = v
	default:
		return newVal, errors.New(fmt.Sprintf("Assert Error: value types:%T cannot trans []Byte", val))
	}
	return newVal, nil
}

func AssertBytesArr(val interface{}) ([]string, error) {
	var stringArr []string
	switch v := val.(type) {
	case []interface{}:
		for _, bytes := range v {
			switch value := bytes.(type) {
			case []byte:
				stringArr = append(stringArr, string(value))
			default:
				return nil, errors.New(fmt.Sprintf("Assert Error: value types:%T cannot trans []Byte", bytes))
			}
		}
	default:
		return nil, errors.New(fmt.Sprintf("Assert Error: value types:%T cannot trans []string", val))
	}
	return stringArr, nil
}

func ProtectRunFunc(f func()) {
	defer TracebackFormatExec(GetFunctionName(f))
	f()
}

func ProtectRunBoolFunc(f func() bool) bool {
	defer TracebackFormatExec(GetFunctionName(f))
	return f()
}

func ProtectRunStringFunc(f func() string) string {
	defer TracebackFormatExec(GetFunctionName(f))
	return f()
}

func GetFunctionName(i interface{}, seps ...rune) string {
	// 获取函数名称
	fn := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
	// 用 seps 进行分割
	fields := strings.FieldsFunc(fn, func(sep rune) bool {
		for _, s := range seps {
			if sep == s {
				return true
			}
		}
		return false
	})

	if size := len(fields); size > 0 {
		return fields[size-1]
	}
	return ""
}
